看看Bitmap源码中recycle 做了什么

看看Bitmap源码中recycle 做了什么

PS:源码查看工具网站:AndroidXref、grepCode

起因

在Bitmap调用recycle之后再去使用,会有invalid Bitmap的异常导致应用崩溃,实际项目中其实是碰到了tgkill的Native代码崩溃的堆栈信息:

分析Bitmap getGenerationId在源码中的调用情况:(git clone Framework 源码之后用sublimeText搜索得如下结果,txt文件已过滤)

绿色框中的代码因为是surface的API,或者是JavaDoc的注释,可以忽略不计,可以看到有效调用就是关于Notification的调用;

结合GP后台的崩溃占用情况(>7.0占比较高,6.0亦有发生),查阅AndroidDeveloper的v6.0 changeLog,发现有这么一条:

看到其中关键信息 ==reuse Notification.Builder instance==,但鉴于其它App没有出现这种级别的问题,所以另谋其它出路;

剩下的就只剩Bitmap了

Bitmap recycle做了什么

Bitmap.cpp

source code on xRef: http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/jni/android/graphics/Bitmap.cpp

1
2
3
4
5
782static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
783 LocalScopedBitmap bitmap(bitmapHandle);
784 bitmap->freePixels();
785 return JNI_TRUE;
786}
1
2
3
4
5
6
7
168void Bitmap::freePixels() {
169 AutoMutex _lock(mLock);
170 if (mPinnedRefCount == 0) {
171 doFreePixels();
172 mPixelStorageType = PixelStorageType::Invalid;
173 }
174}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
176void Bitmap::doFreePixels() {
177 switch (mPixelStorageType) {
178 case PixelStorageType::Invalid:
179 // already free'd, nothing to do
180 break;
181 case PixelStorageType::External:
182 mPixelStorage.external.freeFunc(mPixelStorage.external.address,
183 mPixelStorage.external.context);
184 break;
185 case PixelStorageType::Ashmem:
186 munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
187 close(mPixelStorage.ashmem.fd);
188 break;
189 case PixelStorageType::Java:
190 JNIEnv* env = jniEnv();
191 LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
192 "Deleting a bitmap wrapper while there are outstanding strong "
193 "references! mPinnedRefCount = %d", mPinnedRefCount);
194 env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef);
195 break;
196 }
197
198 if (android::uirenderer::Caches::hasInstance()) {
199 android::uirenderer::Caches::getInstance().textureCache.releaseTexture(
200 mPixelRef->getStableID());
201 }
202}

注意析构函数:

1
2
3
164 Bitmap::~Bitmap() {
165 doFreePixels();
166 }

还需注意到代码:

结合Bitmap.cpp的其它代码,只有在Bitmap recycle之后又拿来使用才会出现这个assertValid断言错误,遂将矛头指向Bitmap recycle

不调用recycle()会怎样?

Android2.3(API 11)之前,Bitmap开辟的内存位于native Stack,需要手动调用recycle进行释放,而2.3之后属于VM的heap,系统GC会自动处理。

而我们在2.3之后的系统中调用Bitmap的recycle,唯一的作用,就是把Bitmap释放的动作提前了,私以为,仅有的效用是会降低OOM发生的概率

这一点也可以从Bitmap AndroidAPI解释网页上看出一点端倪

this is an advanced call, and normally need not be called…

策略

在所有会调用Bitmap.recycle()的地方,对相应的recycle状态检查,如果发现有recycle之后仍然在使用的地方,则直接抛出异常,暴露问题,就像这样:

这样,在Monkey测试覆盖面够广时,就可以预期暴露出问题所在了;

关于Bitmap的检查,还有一种思路是使用hook方式进行,这样还可以对第三方依赖lib进行检查,后期如果应用这种方式,我再补充一篇


Ref:

  1. bitmap recycle不当调用引发的问题 http://www.jianshu.com/p/b5c8e98ff5b0
  2. Bitmap source Code:https://android.googlesource.com/platform/frameworks/base/+/47fb191/core/jni/android/graphics/Bitmap.cpp;(GrepCode网站上5.0r1的Bitmap sourcecode)http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/5.0.0_r2-robolectric-0/android/graphics/Bitmap.java;(AndroidXref网站上的Bitmap 7.1.1 sourcecode)http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/jni/android/graphics/Bitmap.cpp#896
  3. Bitmap API 解释on AndroidDeveloper:https://stuff.mit.edu/afs/sipb/project/android/docs/reference/android/graphics/Bitmap.html#recycle()

Powered by KyleCe

Copyright © 2015 - 2019 KyleCe All Rights Reserved.

访客数 : | 访问量 :